package com.zmj.sy.mom.srv.aps.utils;

import com.zmj.sy.mom.srv.aps.bean.bo.*;
import com.zmj.sy.mom.srv.aps.mapper.ApsDetailWeekdayMapper;
import com.zmj.sy.mom.srv.aps.mapper.ApsWorkDetailMapper;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;

public class ApsTaskUtils {
    public static ApsDetailWeekdayWrap scheduling(ApsSchedulingFactoryCacheBo factoryCache, ApsSchedulingOrderCacheBo orderCache, LocalDateTime startTime, ApsMachine machine, ApsTask currentTask, ApsDetailWeekdayMapper apsDetailWeekdayMapper, ApsWorkDetailMapper apsWorkDetailMapper, List<ApsDetailWeekdayWrap> l) {


        // 计算 工位 并行能力
        Integer allWorkTime = new BigDecimal(currentTask.getApsWorkDetail().getWorkTime())
                .add(new BigDecimal(machine.getStation().getChangTime()))
                .multiply(currentTask.getApsWorkDetail().getTotalCount()) // 乘法
                .add(new BigDecimal(machine.getStation().getEnterTime()))
                .add(new BigDecimal(machine.getStation().getOutTime()))
                .divide(machine.getStation().getWorkAbility(), 0, RoundingMode.UP).intValue(); // 除法

        // 找到包含并算出结束时间的日历
        ApsDetailWeekdayWrap temp = getCalendarWrap(factoryCache, startTime, allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);

        // 找到距离结束时间最近的前一个节点，准备往后排产
        ApsStep prevStep = getMachineTasks(machine, temp.getEndDateTime());

        // 后面如果有节点，则需要判断是否重合
        ApsTask nextStep = prevStep.getNextTask();
        while (nextStep != null) {
            // 如果开始时间在上一个节点的结束时间之后，则需要往后延迟
            if (prevStep.getEndTime().isAfter(temp.getStartDateTime())) {
                temp = getCalendarWrap(factoryCache, prevStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }

            // 如果可以放下，则停止往后排产
            ApsTask t = nextStep;
            boolean overlappingAbutting = false;
            try {
                overlappingAbutting = MkDateTimeUtils.isOverlappingAbutting(temp.getStartDateTime(), temp.getEndDateTime(), t.getPlanStartDateTime(), t.getPlanEndDateTime());
            } catch (Exception e) {
                e.printStackTrace();
                throw e;
            }
            if (overlappingAbutting) {
                break;
            }

            // 如果放不下，则继续往后排
            prevStep = nextStep;
            nextStep = nextStep.getNextTask();
            if (nextStep == null) {
                temp.setStartDateTime(prevStep.getEndTime());
                temp = getCalendarWrap(factoryCache, prevStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
                break;
            } else {
                temp = getCalendarWrap(factoryCache, prevStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }

        }

        // 没有节点的情况下，如果上一个节点的结束时间在我的开始时间之后，则需要往后延迟
        if (prevStep.getEndTime().isAfter(temp.getStartDateTime())) {
            temp = getCalendarWrap(factoryCache, prevStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
        }

        fullCalendarWrap(currentTask, temp, prevStep, nextStep);

        if(temp.getStartDateTime() == null || temp.getEndDateTime() == null){
            throw SyExceptionUtils.e("排产失败:" + JsonUtils.toJsonString(currentTask.getApsWorkDetail()));
        }
        currentTask.getApsWorkDetail().setTotalWorkTime(allWorkTime);
        currentTask.getApsWorkDetail().setEnterTime(machine.getStation().getEnterTime());
        currentTask.getApsWorkDetail().setOutTime(machine.getStation().getOutTime());
        currentTask.getApsWorkDetail().setChangTime(machine.getStation().getChangTime());

        return temp;
    }



    public static void fullCalendarWrap(ApsTask currentTask, ApsDetailWeekdayWrap temp, ApsStep prevStep, ApsTask nextStep) {
        temp.setCurrentTask(currentTask);
        temp.setPrevious(prevStep);
        temp.setNext(nextStep);

    }


    public static ApsStep getMachineTasks(ApsMachine machine, LocalDateTime endTime) {
        List<ApsTask> tasks = new ArrayList<>();
        ApsTask temp = machine.getNextTask();
        while (temp != null) {
            tasks.add(temp);
            temp = temp.getNextTask();
        }

        Optional<ApsTask> max = tasks.stream().filter(e -> !e.getPlanEndDateTime().isAfter(endTime)).max(Comparator.comparing(ApsTask::getPlanEndDateTime));
        ApsStep prevStep = null;
        if (max.isPresent()) {
            prevStep = max.get();
        } else {
            prevStep = machine;
        }

        return prevStep;
    }

    public static void addMachineCalendar(ApsSchedulingFactoryCacheBo factoryCache, ApsMachine machine, ApsDetailWeekdayMapper apsDetailWeekdayMapper, ApsWorkDetailMapper apsWorkDetailMapper) {

        factoryCache.addDay(30, apsDetailWeekdayMapper,apsWorkDetailMapper );
    }
//    public static void addFirstMachineCalendar(ApsSchedulingFactoryCacheBo factoryCache, ApsMachine machine, ApsDetailWeekdayMapper apsDetailWeekdayMapper, ApsWorkDetailMapper apsWorkDetailMapper) {
//
//        factoryCache.addFirstDay(30, apsDetailWeekdayMapper,apsWorkDetailMapper );
//    }

    public static ApsDetailWeekdayWrap getStartCalendarWrap(ApsSchedulingFactoryCacheBo factoryCache, LocalDateTime endTime, Integer taskAllWorkTime, ApsMachine machine, ApsDetailWeekdayMapper apsDetailWeekdayMapper, ApsWorkDetailMapper apsWorkDetailMapper) {
        ApsDetailWeekdayWrap temp = new ApsDetailWeekdayWrap();
        for (int i = machine.getWeekDays().size() - 1; i >= 0 ; i--) {
            ApsWeekDay machineCalendar = machine.getWeekDays().get(i);

            if(endTime == null){
                return null;
            }

            // 如果开始时间在日历的开始时间和结束时间之间，则找到此日历
            if (!(endTime.isBefore(machineCalendar.getStartDateTime()) || endTime.isAfter(machineCalendar.getEndDateTime()))) {
                temp.setEndIndex(i);
                temp.getStartMachineCalendar().add(machineCalendar);
                temp.setEndDateTime(endTime);
                break;
            }

            if(endTime.isAfter(machineCalendar.getStartDateTime()) && endTime.isAfter(machineCalendar.getEndDateTime())){
                temp.setEndIndex(i);
                temp.getStartMachineCalendar().add(machineCalendar);
                temp.setEndDateTime(machineCalendar.getEndDateTime());
                break;
            }

            if (i == 0 && temp.getStartDateTime() == null) {
                temp.setEndIndex(i);
                temp.getStartMachineCalendar().add(machineCalendar);
                temp.setEndDateTime(endTime);
                break;
//                addFirstMachineCalendar(factoryCache ,machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }

        }

        long allWorkTime = taskAllWorkTime;

        if(allWorkTime == 0) {
            temp.setStartIndex(temp.getEndIndex());
            temp.setStartDateTime(temp.getEndDateTime());
            ApsWeekDay machineCalendar = machine.getWeekDays().get(temp.getEndIndex());
            ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
            machineCalendarWrap.setSecond((int)allWorkTime);
            machineCalendarWrap.setStart(temp.getStartDateTime());
            machineCalendarWrap.setEnd(temp.getEndDateTime());
            temp.getAll().add(machineCalendarWrap);
            temp.setMachine(machine);
            return temp;
        }

        for (int i = temp.getEndIndex(); i >= 0; i--) {
            ApsWeekDay machineCalendar = machine.getWeekDays().get(i);
            LocalDateTime start = machineCalendar.getEndDateTime();
            if (i == temp.getEndIndex()) {
                start = temp.getEndDateTime();
            }
            LocalDateTime end = machineCalendar.getStartDateTime();
            long until = end.until(start, ChronoUnit.SECONDS);
            temp.getStartMachineCalendar().add(machineCalendar);
            if (allWorkTime <= until) {
                if (i == 0){
                    ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
                    machineCalendarWrap.setSecond((int)until);
                    machineCalendarWrap.setStart(machineCalendar.getStartDateTime());
                    machineCalendarWrap.setEnd(start);
                    temp.setStartIndex(temp.getEndIndex());
                    temp.setStartDateTime(temp.getEndDateTime());
                    temp.getAll().add(machineCalendarWrap);
                    break;
                }

                temp.setStartIndex(i);
                temp.setStartDateTime(start.minusSeconds(allWorkTime));

                ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
                machineCalendarWrap.setSecond((int)allWorkTime);
                machineCalendarWrap.setStart(temp.getStartDateTime());
                machineCalendarWrap.setEnd(start);
                temp.getAll().add(machineCalendarWrap);
                break;
            }
            allWorkTime -= until;
            if (i == 0 && temp.getStartDateTime() == null) {
//                addFirstMachineCalendar(factoryCache, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
                ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
                machineCalendarWrap.setSecond((int)until);
                machineCalendarWrap.setStart(machineCalendar.getStartDateTime());
                machineCalendarWrap.setEnd(start);
                temp.setStartIndex(temp.getEndIndex());
                temp.setStartDateTime(temp.getEndDateTime());
                temp.getAll().add(machineCalendarWrap);
                break;
            }

            ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
            machineCalendarWrap.setSecond((int)until);
            machineCalendarWrap.setStart(start);
            machineCalendarWrap.setEnd(start.plusSeconds(until));
            temp.getAll().add(machineCalendarWrap);
        }

        temp.setMachine(machine);
//            temp.setStartDateTime(temp.getStartMachineCalendar().get(0).getStartDateTime());
//            temp.setEndDateTime(temp.getStartMachineCalendar().get(temp.getStartMachineCalendar().size() - 1).getEndDateTime());
        return temp;
    }


    public static ApsDetailWeekdayWrap getStartCalendarWrapWuxian(ApsSchedulingFactoryCacheBo factoryCache, LocalDateTime endTime, Integer taskAllWorkTime, ApsMachine machine, ApsDetailWeekdayMapper apsDetailWeekdayMapper, ApsWorkDetailMapper apsWorkDetailMapper) {
        ApsDetailWeekdayWrap temp = new ApsDetailWeekdayWrap();
        for (int i = machine.getWeekDays().size() - 1; i >= 0 ; i--) {
            ApsWeekDay machineCalendar = machine.getWeekDays().get(i);

            if(endTime == null){
                return null;
            }

            // 如果开始时间在日历的开始时间和结束时间之间，则找到此日历
            if (!(endTime.isBefore(machineCalendar.getStartDateTime()) || endTime.isAfter(machineCalendar.getEndDateTime()))) {
                temp.setEndIndex(i);
                temp.getStartMachineCalendar().add(machineCalendar);
                temp.setEndDateTime(endTime);
                break;
            }

            if(endTime.isAfter(machineCalendar.getStartDateTime()) && endTime.isAfter(machineCalendar.getEndDateTime())){
                temp.setEndIndex(i);
                temp.getStartMachineCalendar().add(machineCalendar);
                temp.setEndDateTime(machineCalendar.getEndDateTime());
                break;
            }

            if (i == 0 && temp.getStartDateTime() == null) {
                temp.setEndIndex(i);
                temp.getStartMachineCalendar().add(machineCalendar);
                temp.setEndDateTime(endTime);
                break;
//                addFirstMachineCalendar(factoryCache ,machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }

        }

        long allWorkTime = taskAllWorkTime;

        if(allWorkTime == 0) {
            temp.setStartIndex(temp.getEndIndex());
            temp.setStartDateTime(temp.getEndDateTime());
            ApsWeekDay machineCalendar = machine.getWeekDays().get(temp.getEndIndex());
            ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
            machineCalendarWrap.setSecond((int)allWorkTime);
            machineCalendarWrap.setStart(temp.getStartDateTime());
            machineCalendarWrap.setEnd(temp.getEndDateTime());
            temp.getAll().add(machineCalendarWrap);
            temp.setMachine(machine);
            return temp;
        }

        for (int i = temp.getEndIndex(); i >= 0; i--) {
            ApsWeekDay machineCalendar = machine.getWeekDays().get(i);
            LocalDateTime start = machineCalendar.getEndDateTime();
            if (i == temp.getEndIndex()) {
                start = temp.getEndDateTime();
            }
            LocalDateTime end = machineCalendar.getStartDateTime();
            long until = end.until(start, ChronoUnit.SECONDS);
            temp.getStartMachineCalendar().add(machineCalendar);
            if (allWorkTime <= until) {
                if (i == 0){
                    ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
                    machineCalendarWrap.setSecond((int)until);
                    machineCalendarWrap.setStart(machineCalendar.getStartDateTime());
                    machineCalendarWrap.setEnd(start);
                    temp.setStartIndex(temp.getEndIndex());
                    temp.setStartDateTime(temp.getEndDateTime());
                    temp.getAll().add(machineCalendarWrap);
                    break;
                }

                temp.setStartIndex(i);
                temp.setStartDateTime(start.minusSeconds(allWorkTime));

                ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
                machineCalendarWrap.setSecond((int)allWorkTime);
                machineCalendarWrap.setStart(temp.getStartDateTime());
                machineCalendarWrap.setEnd(start);
                temp.getAll().add(machineCalendarWrap);
                break;
            }
            allWorkTime -= until;
            if (i == 0 && temp.getStartDateTime() == null) {
//                addFirstMachineCalendar(factoryCache, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
                ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
                machineCalendarWrap.setSecond((int)until);
                machineCalendarWrap.setStart(machineCalendar.getStartDateTime());
                machineCalendarWrap.setEnd(start);
                temp.setStartIndex(temp.getEndIndex());
                temp.setStartDateTime(temp.getEndDateTime());
                temp.getAll().add(machineCalendarWrap);
                break;
            }

            ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
            machineCalendarWrap.setSecond((int)until);
            machineCalendarWrap.setStart(start);
            machineCalendarWrap.setEnd(start.plusSeconds(until));
            temp.getAll().add(machineCalendarWrap);
        }

        temp.setMachine(machine);
//            temp.setStartDateTime(temp.getStartMachineCalendar().get(0).getStartDateTime());
//            temp.setEndDateTime(temp.getStartMachineCalendar().get(temp.getStartMachineCalendar().size() - 1).getEndDateTime());
        return temp;
    }

    public static ApsDetailWeekdayWrap getCalendarWrap(ApsSchedulingFactoryCacheBo factoryCache, LocalDateTime startTime, Integer taskAllWorkTime, ApsMachine machine, ApsDetailWeekdayMapper apsDetailWeekdayMapper, ApsWorkDetailMapper apsWorkDetailMapper) {
        ApsDetailWeekdayWrap temp = new ApsDetailWeekdayWrap();
        for (int i = 0; i < machine.getWeekDays().size(); i++) {
            ApsWeekDay machineCalendar = machine.getWeekDays().get(i);

            // 如果开始时间在日历的开始时间和结束时间之间，则找到此日历
            if (!(startTime.isBefore(machineCalendar.getStartDateTime()) || startTime.isAfter(machineCalendar.getEndDateTime()))) {
                temp.setStartIndex(i);
                temp.getStartMachineCalendar().add(machineCalendar);
                temp.setStartDateTime(startTime);
                break;
            }


            if(startTime.isBefore(machineCalendar.getStartDateTime()) && startTime.isBefore(machineCalendar.getEndDateTime())){
                temp.setStartIndex(i);
                temp.getStartMachineCalendar().add(machineCalendar);
                temp.setStartDateTime(machineCalendar.getStartDateTime());
                break;

            }

            if (i == machine.getWeekDays().size() - 1 && temp.getStartDateTime() == null) {
                addMachineCalendar(factoryCache ,machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }

        }

        long allWorkTime = taskAllWorkTime;

        for (int i = temp.getStartIndex(); i < machine.getWeekDays().size(); i++) {
            ApsWeekDay machineCalendar = machine.getWeekDays().get(i);
            LocalDateTime start = machineCalendar.getStartDateTime();
            if (i == temp.getStartIndex()) {
                start = temp.getStartDateTime();
            }
            LocalDateTime end = machineCalendar.getEndDateTime();
            long until = start.until(end, ChronoUnit.SECONDS);
            temp.getStartMachineCalendar().add(machineCalendar);
            if (allWorkTime <= until) {

                temp.setEndIndex(i);
                temp.setEndDateTime(start.plusSeconds(allWorkTime));
                ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
                machineCalendarWrap.setSecond((int)allWorkTime);
                machineCalendarWrap.setStart(start);
                machineCalendarWrap.setEnd(temp.getEndDateTime());
                temp.getAll().add(machineCalendarWrap);
                break;
            }
            allWorkTime -= until;
            if (i == machine.getWeekDays().size() - 1 && temp.getEndDateTime() == null) {
                addMachineCalendar(factoryCache, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }

            ApsWeekDayWrap machineCalendarWrap = MkBeanUtils.copyProperties(machineCalendar, ApsWeekDayWrap.class);
            machineCalendarWrap.setSecond((int)until);
            machineCalendarWrap.setStart(start);
            machineCalendarWrap.setEnd(start.plusSeconds(until));
            temp.getAll().add(machineCalendarWrap);
        }

        temp.setMachine(machine);
//            temp.setStartDateTime(temp.getStartMachineCalendar().get(0).getStartDateTime());
//            temp.setEndDateTime(temp.getStartMachineCalendar().get(temp.getStartMachineCalendar().size() - 1).getEndDateTime());
        return temp;
    }


    public static ApsDetailWeekdayWrap schedulingBottleneckWuxian(ApsSchedulingFactoryCacheBo factoryCache, ApsSchedulingOrderCacheBo orderCache, LocalDateTime startTime, ApsMachine machine, ApsTask currentTask, ApsDetailWeekdayMapper apsDetailWeekdayMapper, ApsWorkDetailMapper apsWorkDetailMapper) {

        // 计算 工位 并行能力
        Integer allWorkTime = new BigDecimal(currentTask.getApsWorkDetail().getWorkTime()) // 实动工时
                .add(new BigDecimal(machine.getStation().getChangTime())) // 加间隔工时
                .multiply(currentTask.getApsWorkDetail().getTotalCount()) // 乘以数量
                .add(new BigDecimal(machine.getStation().getEnterTime())) // 加转入时间
                .add(new BigDecimal(machine.getStation().getOutTime())) // 加转出时间
                .divide(machine.getStation().getWorkAbility(), 0, RoundingMode.UP).intValue(); // 除以作业能力

        // 找到包含并算出结束时间的日历
        ApsDetailWeekdayWrap temp = getStartCalendarWrapWuxian(factoryCache, startTime, allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);

        // 如果没有任务，随意放
//        if(machine.getNextTask() == null){
            fullCalendarWrap(currentTask, temp,  machine, null);
            if(temp.getStartDateTime() == null || temp.getEndDateTime() == null){
                throw SyExceptionUtils.e("排产失败:" + JsonUtils.toJsonString(currentTask.getApsWorkDetail()));
            }
            currentTask.getApsWorkDetail().setTotalWorkTime(allWorkTime);
            currentTask.getApsWorkDetail().setEnterTime(machine.getStation().getEnterTime());
            currentTask.getApsWorkDetail().setOutTime(machine.getStation().getOutTime());
            currentTask.getApsWorkDetail().setChangTime(machine.getStation().getChangTime());
            return temp;
//        }

        /*

        // 找到距离结束时间最近一个节点，准备往前排产
        ApsTask nextStep = getMachineTasks2(machine, temp.getEndDateTime());
        ApsStep prevStep = null;
        if (nextStep == null) {
            prevStep = getLastTask(machine);
            nextStep = machine.getNextTask();

            fullCalendarWrap(currentTask, temp,  prevStep, nextStep);

            if(temp.getStartDateTime() == null || temp.getEndDateTime() == null){
                throw SyExceptionUtils.e("排产失败:" + JsonUtils.toJsonString(currentTask.getApsWorkDetail()));
            }
            currentTask.getApsWorkDetail().setTotalWorkTime(allWorkTime);
            currentTask.getApsWorkDetail().setEnterTime(machine.getStation().getEnterTime());
            currentTask.getApsWorkDetail().setOutTime(machine.getStation().getOutTime());
            currentTask.getApsWorkDetail().setChangTime(machine.getStation().getChangTime());
            if(temp.getPrevious() == null){
                temp.setPrevious(machine);
            }
            return temp;
        }

        prevStep = nextStep.getPreviewTask();


        while (nextStep != null) {

            if(prevStep == null){
                if (temp.getEndDateTime().isAfter(nextStep.getPlanStartDateTime())) {
                    temp = getStartCalendarWrap(factoryCache, nextStep.getPlanStartDateTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
                } else {
                    temp = getStartCalendarWrap(factoryCache, startTime, allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
                }
                break;
            }

            if (temp.getEndDateTime().isAfter(nextStep.getPlanStartDateTime())) {
                temp = getStartCalendarWrap(factoryCache, nextStep.getPlanStartDateTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }


            if(temp.getStartDateTime() == null || temp.getEndDateTime() == null || ((ApsTask)prevStep).getPlanStartDateTime() == null || ((ApsTask)prevStep).getPlanEndDateTime() == null){
                throw SyExceptionUtils.e("时间参数不能为空");
            }

            boolean overlappingAbutting = MkDateTimeUtils.isOverlappingAbutting(temp.getStartDateTime(), temp.getEndDateTime(), ((ApsTask)prevStep).getPlanStartDateTime(), ((ApsTask)prevStep).getPlanEndDateTime());
            if (overlappingAbutting) {
                break;
            }

            nextStep = (ApsTask) prevStep;
            prevStep = nextStep.getPreviewTask();
        }
        fullCalendarWrap(currentTask, temp, prevStep, nextStep);

        if(temp.getStartDateTime() == null || temp.getEndDateTime() == null){
            throw SyExceptionUtils.e("排产失败:" + JsonUtils.toJsonString(currentTask.getApsWorkDetail()));
        }
        currentTask.getApsWorkDetail().setTotalWorkTime(allWorkTime);
        currentTask.getApsWorkDetail().setEnterTime(machine.getStation().getEnterTime());
        currentTask.getApsWorkDetail().setOutTime(machine.getStation().getOutTime());
        currentTask.getApsWorkDetail().setChangTime(machine.getStation().getChangTime());
        if(temp.getPrevious() == null){
            temp.setPrevious(machine);
        }


        return temp;

         */

        /*
        // 后面如果有节点，则需要判断是否重合
        ApsTask prevStep = null;
        while (nextStep != null ) {
            // 如果开始时间在上一个节点的结束时间之后，则需要往后延迟
            if (prevStep.getEndTime().isAfter(temp.getStartDateTime())) {
                temp = getCalendarWrap(apsFactoryCache, prevStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }

            // 如果可以放下，则停止往后排产
            ApsTask t = nextStep;
            boolean overlappingAbutting = false;
            try {
                overlappingAbutting = MkDateTimeUtils.isOverlappingAbutting(temp.getStartDateTime(), temp.getEndDateTime(), t.getPlanStartDateTime(), t.getPlanEndDateTime());
            } catch (Exception e) {
                e.printStackTrace();
                throw e;
            }
            if (overlappingAbutting) {
                break;
            }

            // 如果放不下，则继续往后排
            prevStep = nextStep;
            nextStep = nextStep.getNextTask();
            if (nextStep == null) {
                temp.setStartDateTime(prevStep.getEndTime());
                temp = getCalendarWrap(apsFactoryCache, prevStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
                break;
            } else {
                temp = getCalendarWrap(apsFactoryCache, nextStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }

        }

        // 没有节点的情况下，如果上一个节点的结束时间在我的开始时间之后，则需要往后延迟
        try {
            if (prevStep.getEndTime().isAfter(temp.getStartDateTime())) {
                temp = getCalendarWrap(apsFactoryCache, prevStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }
        } catch (Exception e){
            throw e;
        }
        fullCalendarWrap(currentTask, temp,  prevStep, (ApsTask)nextStep);

        if(temp.getStartDateTime() == null || temp.getEndDateTime() == null){
            throw SyExceptionUtils.e("排产失败:" + JsonUtils.toJsonString(currentTask.getApsWorkDetail()));
        }
        currentTask.getApsWorkDetail().setTotalWorkTime(allWorkTime);
        currentTask.getApsWorkDetail().setEnterTime(machine.getStation().getEnterTime());
        currentTask.getApsWorkDetail().setOutTime(machine.getStation().getOutTime());
        currentTask.getApsWorkDetail().setChangTime(machine.getStation().getChangTime());

        return temp;

         */
    }

    public static ApsDetailWeekdayWrap schedulingBottleneck(ApsSchedulingFactoryCacheBo factoryCache, ApsSchedulingOrderCacheBo orderCache, LocalDateTime startTime, ApsMachine machine, ApsTask currentTask, ApsDetailWeekdayMapper apsDetailWeekdayMapper, ApsWorkDetailMapper apsWorkDetailMapper) {

        // 计算 工位 并行能力
        Integer allWorkTime = new BigDecimal(currentTask.getApsWorkDetail().getWorkTime()) // 实动工时
                .add(new BigDecimal(machine.getStation().getChangTime())) // 加间隔工时
                .multiply(currentTask.getApsWorkDetail().getTotalCount()) // 乘以数量
                .add(new BigDecimal(machine.getStation().getEnterTime())) // 加转入时间
                .add(new BigDecimal(machine.getStation().getOutTime())) // 加转出时间
                .divide(machine.getStation().getWorkAbility(), 0, RoundingMode.UP).intValue(); // 除以作业能力

        // 找到包含并算出结束时间的日历
        ApsDetailWeekdayWrap temp = getStartCalendarWrap(factoryCache, startTime, allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);

        // 如果没有任务，随意放
        if(machine.getNextTask() == null){
            fullCalendarWrap(currentTask, temp,  machine, null);
            if(temp.getStartDateTime() == null || temp.getEndDateTime() == null){
                throw SyExceptionUtils.e("排产失败:" + JsonUtils.toJsonString(currentTask.getApsWorkDetail()));
            }
            currentTask.getApsWorkDetail().setTotalWorkTime(allWorkTime);
            currentTask.getApsWorkDetail().setEnterTime(machine.getStation().getEnterTime());
            currentTask.getApsWorkDetail().setOutTime(machine.getStation().getOutTime());
            currentTask.getApsWorkDetail().setChangTime(machine.getStation().getChangTime());
            return temp;
        }


        // 找到距离结束时间最近一个节点，准备往前排产
        ApsTask nextStep = getMachineTasks2(machine, temp.getEndDateTime());
        ApsStep prevStep = null;
        if (nextStep == null) {
            prevStep = getLastTask(machine);
            nextStep = machine.getNextTask();

            fullCalendarWrap(currentTask, temp,  prevStep, nextStep);

            if(temp.getStartDateTime() == null || temp.getEndDateTime() == null){
                throw SyExceptionUtils.e("排产失败:" + JsonUtils.toJsonString(currentTask.getApsWorkDetail()));
            }
            currentTask.getApsWorkDetail().setTotalWorkTime(allWorkTime);
            currentTask.getApsWorkDetail().setEnterTime(machine.getStation().getEnterTime());
            currentTask.getApsWorkDetail().setOutTime(machine.getStation().getOutTime());
            currentTask.getApsWorkDetail().setChangTime(machine.getStation().getChangTime());
            if(temp.getPrevious() == null){
                temp.setPrevious(machine);
            }
            return temp;
        }

        prevStep = nextStep.getPreviewTask();


        while (nextStep != null) {

            if(prevStep == null){
                if (temp.getEndDateTime().isAfter(nextStep.getPlanStartDateTime())) {
                    temp = getStartCalendarWrap(factoryCache, nextStep.getPlanStartDateTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
                } else {
                    temp = getStartCalendarWrap(factoryCache, startTime, allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
                }
                break;
            }

            if (temp.getEndDateTime().isAfter(nextStep.getPlanStartDateTime())) {
                temp = getStartCalendarWrap(factoryCache, nextStep.getPlanStartDateTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }


            if(temp.getStartDateTime() == null || temp.getEndDateTime() == null || ((ApsTask)prevStep).getPlanStartDateTime() == null || ((ApsTask)prevStep).getPlanEndDateTime() == null){
                throw SyExceptionUtils.e("时间参数不能为空");
            }

            boolean overlappingAbutting = MkDateTimeUtils.isOverlappingAbutting(temp.getStartDateTime(), temp.getEndDateTime(), ((ApsTask)prevStep).getPlanStartDateTime(), ((ApsTask)prevStep).getPlanEndDateTime());
            if (overlappingAbutting) {
                break;
            }

            nextStep = (ApsTask) prevStep;
            prevStep = nextStep.getPreviewTask();
        }
        fullCalendarWrap(currentTask, temp, prevStep, nextStep);

        if(temp.getStartDateTime() == null || temp.getEndDateTime() == null){
            throw SyExceptionUtils.e("排产失败:" + JsonUtils.toJsonString(currentTask.getApsWorkDetail()));
        }
        currentTask.getApsWorkDetail().setTotalWorkTime(allWorkTime);
        currentTask.getApsWorkDetail().setEnterTime(machine.getStation().getEnterTime());
        currentTask.getApsWorkDetail().setOutTime(machine.getStation().getOutTime());
        currentTask.getApsWorkDetail().setChangTime(machine.getStation().getChangTime());
        if(temp.getPrevious() == null){
            temp.setPrevious(machine);
        }


        return temp;

        /*
        // 后面如果有节点，则需要判断是否重合
        ApsTask prevStep = null;
        while (nextStep != null ) {
            // 如果开始时间在上一个节点的结束时间之后，则需要往后延迟
            if (prevStep.getEndTime().isAfter(temp.getStartDateTime())) {
                temp = getCalendarWrap(apsFactoryCache, prevStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }

            // 如果可以放下，则停止往后排产
            ApsTask t = nextStep;
            boolean overlappingAbutting = false;
            try {
                overlappingAbutting = MkDateTimeUtils.isOverlappingAbutting(temp.getStartDateTime(), temp.getEndDateTime(), t.getPlanStartDateTime(), t.getPlanEndDateTime());
            } catch (Exception e) {
                e.printStackTrace();
                throw e;
            }
            if (overlappingAbutting) {
                break;
            }

            // 如果放不下，则继续往后排
            prevStep = nextStep;
            nextStep = nextStep.getNextTask();
            if (nextStep == null) {
                temp.setStartDateTime(prevStep.getEndTime());
                temp = getCalendarWrap(apsFactoryCache, prevStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
                break;
            } else {
                temp = getCalendarWrap(apsFactoryCache, nextStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }

        }

        // 没有节点的情况下，如果上一个节点的结束时间在我的开始时间之后，则需要往后延迟
        try {
            if (prevStep.getEndTime().isAfter(temp.getStartDateTime())) {
                temp = getCalendarWrap(apsFactoryCache, prevStep.getEndTime(), allWorkTime, machine, apsDetailWeekdayMapper,apsWorkDetailMapper);
            }
        } catch (Exception e){
            throw e;
        }
        fullCalendarWrap(currentTask, temp,  prevStep, (ApsTask)nextStep);

        if(temp.getStartDateTime() == null || temp.getEndDateTime() == null){
            throw SyExceptionUtils.e("排产失败:" + JsonUtils.toJsonString(currentTask.getApsWorkDetail()));
        }
        currentTask.getApsWorkDetail().setTotalWorkTime(allWorkTime);
        currentTask.getApsWorkDetail().setEnterTime(machine.getStation().getEnterTime());
        currentTask.getApsWorkDetail().setOutTime(machine.getStation().getOutTime());
        currentTask.getApsWorkDetail().setChangTime(machine.getStation().getChangTime());

        return temp;

         */
    }


    public static ApsTask getMachineTasks2(ApsMachine machine, LocalDateTime dateTime) {
        List<ApsTask> tasks = new ArrayList<>();
        ApsTask temp = machine.getNextTask();
        ApsStep previewTask = null;
        while (temp != null) {
            tasks.add(temp);
            temp.setPreviewTask(previewTask);
            previewTask = temp;
            temp = temp.getNextTask();

        }

        Optional<ApsTask> max = tasks.stream().filter(e -> !e.getPlanEndDateTime().isBefore(dateTime)).min(Comparator.comparing(ApsTask::getPlanEndDateTime));
        ApsTask prevStep = null;
        if (max.isPresent()) {
            prevStep = max.get();
        }

        return prevStep;
    }

    public static ApsTask getLastTask(ApsMachine machine) {
        if(machine.getNextTask() == null){
            return null;
        }
        ApsTask last = machine.getNextTask();
        while (last != null) {
            last = last.getNextTask();
        }
        return last;
    }
}
